package gm.editor.scripting;

import gm.editor.HoleComponent;
import gm.editor.KanavaPanel;
import gm.editor.StitchComponent;
import gm.editor.WoolComponent;
import gm.data.HoleColor;
import gm.data.Wool;
import java.awt.Color;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

/**
 * Kanava szkriptelését elősegítő osztály.
 *
 * @author imruf84
 */
public class KanavaAPI {

    /**
     * Kanavapanel.
     */
    private final KanavaPanel kp;
    /**
     * Hibakonzol.
     */
    private final JTextPane out;

    /**
     * Konstrukor.
     *
     * @param kp kanavapanel
     * @param out hibakonzol
     */
    public KanavaAPI(KanavaPanel kp, JTextPane out) {
        this.kp = kp;
        this.out = out;
    }

    /**
     * Kimenet szövegének görgetése a végére.
     */
    private void scrollOutToEnd() {
        out.setCaretPosition(out.getDocument().getLength());
    }

    /**
     * Hibaüzenet kiírása a konzolra.
     *
     * @param msg hibaüzenet szövege
     */
    public void writeErrorMessage(String msg) {

        // Hiba szó.
        String errorWord = "HIBA";

        StyledDocument doc = this.out.getStyledDocument();
        Style style = this.out.addStyle("ERROR", null);
        StyleConstants.setForeground(style, Color.red);

        try {
            // A hibaüzenet mindig külön sorba kerül.
            if (!this.out.getText().isEmpty() && '\n' != this.out.getText().charAt(this.out.getText().length() - 1)) {
                doc.insertString(doc.getLength(), "\n", style);
            }

            doc.insertString(doc.getLength(), errorWord + ":" + msg + "\n", style);
        } catch (BadLocationException ex) {
            // Sikertelen formázás esetén csak megjelenítjük egyszerűen a szöveget.
            this.out.setText(this.out.getText() + errorWord + ":" + msg + "\n");
        }

        scrollOutToEnd();
    }

    /**
     * Figyelmeztetés kiírása a konzolra.
     *
     * @param msg figyelmeztetés szövege
     */
    public void writeWarningMessage(String msg) {

        // Hiba szó.
        String warningWord = "FIGYELEM";

        StyledDocument doc = this.out.getStyledDocument();
        Style style = this.out.addStyle("WARNING", null);
        StyleConstants.setForeground(style, Color.magenta);

        try {
            // A figyelmeztetés mindig külön sorba kerül.
            if (!this.out.getText().isEmpty() && '\n' != this.out.getText().charAt(this.out.getText().length() - 1)) {
                doc.insertString(doc.getLength(), "\n", style);
            }

            doc.insertString(doc.getLength(), warningWord + ":" + msg + "\n", style);
        } catch (BadLocationException ex) {
            // Sikertelen formázás esetén csak megjelenítjük egyszerűen a szöveget.
            this.out.setText(this.out.getText() + warningWord + ":" + msg + "\n");
        }

        scrollOutToEnd();
    }

    /**
     * Üzenet kiírása a konzolra.
     *
     * @param msg üzenet szövege
     */
    public void writeMessage(String msg) {

        StyledDocument doc = this.out.getStyledDocument();
        Style style = this.out.addStyle("NORMAL", null);
        StyleConstants.setForeground(style, Color.black);

        try {
            doc.insertString(doc.getLength(), msg, style);
        } catch (BadLocationException ex) {
            // Sikertelen formázás esetén csak megjelenítjük egyszerűen a szöveget.
            this.out.setText(this.out.getText() + msg);
        }

        scrollOutToEnd();
    }

    /**
     * Kimenet tartalmának törlése.
     */
    public void clearOutput() {
        writeMessage("");
        this.out.setText("");
    }

    /**
     * Sorok számának a lekérdezése.
     *
     * @return sorok száma
     */
    public int rowsCount() {
        return this.kp.getKanava().getRowsCount();
    }

    /**
     * Oszlopok számának a lekérdezése.
     *
     * @return oszlopok száma
     */
    public int columnsCount() {
        return this.kp.getKanava().getColumnsCount();
    }

    /**
     * Új szín definiálása.
     *
     * @param s szimbólum
     * @param r vörös
     * @param g zöld
     * @param b kék
     */
    public void addColor(char s, int r, int g, int b) {

        // Ha van már ilyen szín, akkor figyelmeztetünk.
        if (null != kp.getKanava().getHoleColor(s)) {
            this.writeWarningMessage("Már létezik szín ilyen azonosítóval:'" + s + "' (felülírva)");
        }

        // Mehet a munka.
        this.kp.addHoleColor(new HoleColor(new Color(r, g, b), s), false, true);
    }

    /**
     * Szín törlése.
     *
     * @param s szimbólum
     */
    public void removeColor(char s) {

        // Háttérszínt nem törölhetünk.
        if (' ' == s) {
            this.writeWarningMessage("Háttérszín nem törölhető.");
            return;
        }

        // Ha nincs ilyen szín, akkor figyelmeztetünk és kilépünk.
        HoleColor hc = kp.getKanava().getHoleColor(s);
        if (null == hc) {
            this.writeWarningMessage("Nem létezik szín ilyen azonosítóval:'" + s + "' (nem lesz törölve semmi)");
            return;
        }

        // Mehet a munka.
        this.kp.removeHoleColor(hc, -1, false);
    }

    /**
     * Lyuk színének megadása.
     *
     * @param r sor
     * @param c oszlop
     * @param s szín betűjele
     */
    public void setHoleColor(int r, int c, char s) {
        // Lyuk lekérdezése.
        HoleComponent hc = kp.getHoleComponent(r, c);
        // Ha nincs ilyen lyuk, akkor kilépünk.
        if (null == hc) {
            this.writeWarningMessage("Nem található lyuk ezen a koordinátán:[" + r + "," + c + "]");
            return;
        }

        // Szín lekérdezése.
        HoleColor hcolor = kp.getKanava().getHoleColor(s);
        // Ha nincs ilyen szín, akkor kilépünk.
        if (null == hcolor) {
            this.writeWarningMessage("Nem található szín ezzel az azonosítóval:'" + s + "'");
            return;
        }

        // Egyébként mehet a munka.
        kp.setHoleComponentColor(hc, hcolor);
    }

    /**
     * Lyuk színének alapértelmezettre állítása.
     *
     * @param r sor
     * @param c oszlop
     */
    public void clearHoleColor(int r, int c) {
        setHoleColor(r, c, ' ');
    }

    private WoolComponent getWoolComponent(char s) {

        // Fonal lekérdezése.
        WoolComponent wc = this.kp.getWoolComponent(s);

        // Ha nincs ilyen fonal akkor hibával leállunk.
        if (null == wc) {
            this.writeWarningMessage("Nem létezik fonal ilyen azonosítójú színnel:'" + s + "'");
        }

        return wc;
    }

    /**
     * Öltés hozzáadása a fonal végéhez.
     *
     * @param s fonal színének azonosítója
     * @param pr sor
     * @param pc oszlop
     * @param mode 'a' érték estén abszolút hivatkozás, 'r' esetén relatív
     * havatkozás az utolsó öltéshez képest
     */
    public void appendStitch(char s, int pr, int pc, char mode) {

        // Fonal lekérdezése.
        WoolComponent wc = this.getWoolComponent(s);
        // Ha nincs ilyen fonal akkor kilépünk.
        if (null == wc) {
            return;
        }

        // Utolsó öltés lekérdezése.
        StitchComponent lsc = wc.getLastStitchComponent();

        // Alapesetben abszolút koordinátákat használunk...
        int r = pr, c = pc;
        // ...ha létezik utolsó öltés akkor relatívakat.
        if ((null != lsc) && ('r' == mode)) {
            // Abszolút koordináták kiszámítása.
            r = lsc.getStitch().getRow() + pr;
            c = lsc.getStitch().getColumn() + pc;
        }

        // Adott öltés lekérdezése.
        StitchComponent sc = this.kp.getStitchComponent(r, c, true, false);

        // Ha nincs ilyen öltés akkor hibával kilépünk.
        if (null == sc) {
            this.writeWarningMessage("Nincs szabad öltés az adott koordinátán:[" + r + "," + c + "]");
            return;
        }

        // Ha az öltés már foglalt akkor jelezzük.
        if (null != sc.getStitch().getWool()) {
            // Ha az adott fonalhoz tartozik...
            if (sc.getStitch().getWool().equals(wc.getWool())) {
                this.writeWarningMessage("Az öltés már az adott azonosítójú színhez tartozik:'" + s + "'");
            } else {
                // ...vagy más fonalhoz tartozik.
                this.writeWarningMessage("Az öltés már egy másik azonosítójú színhez tartozik:'" + sc.getStitch().getWool().getColor().getSymbol() + "'");
            }

            return;
        }

        // Öltés tárolása.
        sc.addToWoolComponent(wc, true, true);
    }

    /**
     * Fonal utolsó öltésének az eltávolítása.
     *
     * @param s fonal színének azonosítója
     */
    public void removeLastStitch(char s) {

        // Fonal lekérdezése.
        WoolComponent wc = this.getWoolComponent(s);
        // Ha nincs ilyen fonal akkor kilépünk.
        if (null == wc) {
            return;
        }

        // Utolsó öltés lekérdezése.
        StitchComponent lsc = wc.getLastStitchComponent();

        // Ha nem létezik utolsó öltés akkor kilépünk.
        if (null == lsc) {
            this.writeWarningMessage("Az adott azonosítójú színhez tartozó fonal nem tartalmaz öltéseket:'" + s + "'");
            return;
        }

        // Öltés eltávolítása.
        this.kp.removeLastStitch(wc);
    }

    /**
     * Öltés irányának megfordítása.
     *
     * @param r sor
     * @param c oszlop
     */
    public void reverseStitch(int r, int c) {

        // Csomó lekérdezése.
        StitchComponent sc = this.kp.getStitchComponent(r, c, false, true);
        // Ha nincs ilyen csomó, akkor kilépünk.
        if (null == sc) {
            this.writeErrorMessage("Nincs fonalhoz tartozó öltés ilyen koordinátán:[" + r + "," + c + "]");
            return;
        }

        // Ha a csomó nem tartozik fonalhoz, akkor kilépünk.
        Wool w = sc.getStitch().getWool();
        if (null == w) {
            this.writeWarningMessage("Az öltés [" + r + "," + c + "] nem tartozik egyetlen fonalhoz sem (az irány nem változik).");
            return;
        }

        // Fonal lekérdezése.
        WoolComponent wc = this.getWoolComponent(w.getColor().getSymbol());

        // Mehet a munka.
        this.kp.reverseStitch(sc, wc);
    }

}
